package com.blackcat.demo.jdk;

import com.blackcat.demo.comm.User;
import org.junit.jupiter.api.Test;

import java.util.Optional;

import static org.junit.jupiter.api.Assertions.*;

/**
 * @Title Optional 类示例
 * @Description Optional 类主要解决的问题是臭名昭著的空指针异常（NullPointerException）
 * @author blackcat
 * @date 2020年12月07日 14:59
 * @version V1.0
 * @link https://www.cnblogs.com/zhangboyu/p/7580262.html
 * @since V1.0
 */
public class OptionalExample {

	User user = new User();

	@Test
	void test(){

		// 执行该代码,会有空指针错误
		String isocode1 = user.getAddress().getCountry().getIsoCode().toUpperCase();

		// 如果需要判断空值是需要以下写法，就变得冗长，难以维护。
		/*if (user != null) {
			Address address = user.getAddress();
			if (address != null) {
				Country country = address.getCountry();
				if (country != null) {
					String isocode = country.getIsoCode();
					if (isocode != null) {
						isocode = isocode.toUpperCase();
					}
				}
			}
		}*/

	}

	/**
	 * <p> 描述 : empty()该方法用来构造一个空的 Optional，即该 Optional 中不包含值
	 * @author : blackcat
	 * @date  : 2020/12/7 15:51
	*/
	@Test
	public void whenCreateEmptyOptional_thenNull() {
		Optional<User> emptyOpt = Optional.empty();
		// Optional 的 isPresent() 方法用来判断是否包含值，get() 用来获取 Optional 包含的值。
		// 如果值不存在，即在一个Optional.empty 上调用 get() 方法的话，将会抛出 NoSuchElementException 异常。
		emptyOpt.get();
	}

	/**
	 * <p> 描述 : of() 和 ofNullable() 方法创建包含值的 Optional
	 * 		两个方法的不同之处在于如果你把 null 值作为参数传递进去，of() 方法会抛出 NullPointerException：
	 * @author : blackcat
	 * @date  : 2020/12/7 15:54
	*/
	@Test
	public void whenCreateOfEmptyOptional_thenNullPointerException() {
		Optional<User> opt = Optional.of(user);

		// 如果对象即可能是 null 也可能是非 null，你就应该使用 ofNullable() 方法
		// Optional<User> opt = Optional.ofNullable(user);
	}


	/**
	 * <p> 描述 : 访问 Optional 对象的值
	 * 		这个方法会在值为 null 的时候抛出异常
	 * @author : blackcat
	 * @date  : 2020/12/7 15:57
	*/
	@Test
	public void whenCreateOfNullableOptional_thenOk() {
		String name = "John";
		Optional<String> opt = Optional.ofNullable(name);
//		Optional<String> opt = Optional.ofNullable(null);
		assertEquals("John", opt.get());
	}

	/**
	 * <p> 描述 : 判断是否有值
	 * 		这个方法会在值为 null 的时候抛出异常
	 * @author : blackcat
	 * @date  : 2020/12/7 15:57
	 */
	@Test
	public void whenCheckIfPresent_thenOk() {
		User user = new User("111111", "1234");
		Optional<User> opt = Optional.ofNullable(user);

		// 方法一：
//		assertTrue(opt.isPresent());
//		assertEquals(user.getUserPassword(), opt.get().getUserPassword());

		/**
		 * 方法二：
		 * ifPresent 该方法除了执行检查，还接受一个Consumer(消费者) 参数，如果对象不是空的，就对执行传入的 Lambda 表达式
		 * 这个例子中，只有 user 用户不为 null 的时候才会执行断言。
		 * */
		 opt.ifPresent( u -> assertEquals(user.getUserPassword(), u.getUserPassword()));
	}

	/**
	 * <p> 描述 : 返回默认值
	 * 		orElse 如果有值则返回该值，否则返回传递给它的参数值
	 * @author : blackcat
	 * @date  : 2020/12/7 16:05
	*/
	@Test
	public void whenEmptyValue_thenReturnDefault() {
		User user = null;
		User user2 = new User("111111", "1234");
		// 这里 user 对象是空的，所以返回了作为默认值的 user2。
		User result = Optional.ofNullable(user).orElse(user2);

		assertEquals(user2.getUserPassword(), result.getUserPassword());
	}

	/**
	 * 描述 : 返回默认值
	 * 		如果对象的初始值不是 null，那么默认值会被忽略
	 * @author : blackcat
	 * @date : 2021/5/21 9:54
	*/
	@Test
	public void whenValueNotNull_thenIgnoreDefault() {
		User user = new User("john@gmail.com","1234");
		User user2 = new User("anna@gmail.com", "1234");
		User result = Optional.ofNullable(user).orElse(user2);

		assertEquals("john@gmail.com", result.getUsername());

		// 第二个同类型的 API 是 orElseGet() —— 其行为略有不同。这个方法会在有值的时候返回值，
		// 如果没有值，它会执行作为参数传入的 Supplier(供应者) 函数式接口，并将返回其执行结果：
		// User result = Optional.ofNullable(user).orElseGet( () -> user2);
	}

	/**
	 * 描述 : orElse() 和 orElseGet() 的不同之处
	 * 		两种方法都调用了 createNewUser() 方法，这个方法会记录一个消息并返回 User 对象。
	 * 		由此可见，当对象为空而返回默认对象时，行为并无差异。
	 * @author : zhangdahui
	 * @date : 2021/5/21 9:59
	*/
	@Test
	public void givenEmptyValue_whenCompare_thenOk() {
		User user = null;
		System.out.println("Using orElse");
		User result = Optional.ofNullable(user).orElse(createNewUser());
		System.out.println("Using orElseGet");
		User result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser());
	}

	/**
	 * 描述 : orElse() 和 orElseGet() 的不同之处
	 * 		两个 Optional  对象都包含非空值，两个方法都会返回对应的非空值。
	 * 		不过，orElse() 方法仍然创建了 User 对象。与之相反，orElseGet() 方法不创建 User 对象。
	 * 		在执行较密集的调用时，比如调用 Web 服务或数据查询，这个差异会对性能产生重大影响。
	 * @author : blackcat
	 * @date : 2021/5/21 9:59
	 */
	@Test
	public void givenPresentValue_whenCompare_thenOk() {
		User user = new User("john@gmail.com", "1234");
		System.out.println("Using orElse");
		User result = Optional.ofNullable(user).orElse(createNewUser());
		System.out.println("Using orElseGet");
		User result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser());
	}

	private User createNewUser() {
		System.out.println("Creating New User");
		return new User("extra@gmail.com", "1234");
	}

	/**
	 * 描述 : 返回异常
	 * 		在对象为空的时候抛出异常，而不是返回备选的值：
	 * 		这里，如果 user 值为 null，会抛出 IllegalArgumentException。
	 * 		这个方法让我们有更丰富的语义，可以决定抛出什么样的异常，而不总是抛出 NullPointerException。
	 * @author : blackcat
	 * @date : 2021/5/21 10:34
	*/
	@Test
	public void whenThrowException_thenOk(){
		user = null;
		User result = Optional.ofNullable(user)
				.orElseThrow( () -> new IllegalArgumentException());
//		User result = Optional.ofNullable(user)
//				.orElseThrow(() ->
//					assertThrows(IllegalArgumentException.class, () -> new IllegalArgumentException("a message"))
//				);
	}

	/**
	 * 描述 : 转换值
	 *		map() 对值应用(调用)作为参数的函数，然后将返回的值包装在 Optional 中。
	 * @author : blackcat
	 * @date : 2021/5/21 10:36
	*/
	@Test
	public void whenMap_thenOk() {
		User user = new User("anna@gmail.com", "1234");
		String email = Optional.ofNullable(user)
				.map(u -> u.getUsername()).orElse("default@gmail.com");

		assertEquals(email, user.getUsername());
	}

	/**
	 * 描述 :  转换值
	 * 		flatMap() 也需要函数作为参数，并对值调用这个函数，然后直接返回结果。
	 * @author : blackcat
	 * @date : 2021/5/21 10:38
	*/
	@Test
	public void whenFlatMap_thenOk() {
		User user = new User("anna@gmail.com", "1234");
		user.setPosition("Developer");
		String position = Optional.ofNullable(user)
				.flatMap(u -> u.getPosition()).orElse("default");

		assertEquals(position, user.getPosition().get());
	}

	/**
	 * 描述 : 过滤值
	 * 		filter() 接受一个 Predicate 参数，返回测试结果为 true 的值。
	 * 		如果测试结果为 false，会返回一个空的 Optional。
	 * 		如果通过过滤器测试，result 对象会包含非空值。
	 * @author : blackcat
	 * @date : 2021/5/21 10:40
	*/
	@Test
	public void whenFilter_thenOk() {
		User user = new User("anna@gmail.com", "1234");
		Optional<User> result = Optional.ofNullable(user)
				.filter(u -> u.getUsername() != null && u.getUsername().contains("@"));

		assertTrue(result.isPresent());
	}

	/**
	 * 描述 : Optional 类的链式方法
	 * @author : blackcat
	 * @date : 2021/5/21 10:51
	*/
	@Test
	public void whenChaining_thenOk() {
		User user = new User("anna@gmail.com", "1234");

		String result = Optional.ofNullable(user)
				.flatMap(u -> u.getAddress2())
				.flatMap(a -> a.getCountry())
				.map(c -> c.getIsoCode())
				.orElse("default");

		assertEquals(result, "default");
	}

}
